#include "HTTPParser.h"
#include <assert.h>
#include <algorithm>
#include <string.h>
#include <stdarg.h>

namespace Ark
{
	/* http parser callbacks */
	int HTTPParser::on_message_begin(http_parser* parser)
	{
		HTTPParser* _this = (HTTPParser*)parser->data;
		_this->_context.state = STATE_RECV_BEGIN;
		return 0;
	}

	int HTTPParser::on_status(http_parser* parser, const char *at, size_t length)
	{
		HTTPParser* _this = (HTTPParser*)parser->data;
		_this->_statusCode = parser->status_code;
		_this->_statusText.append(at, at + length);
		return 0;
	}

	// NOTICE: on_header_field may be called multiple times for one http header field.
	int HTTPParser::on_header_field(http_parser* parser, const char* at, size_t length)
	{
		HTTPParser* _this = (HTTPParser*)parser->data;
		auto& state = _this->_context.state;
		auto& ctx = _this->_context;

		if (state == STATE_RECV_BEGIN)
		{
			assert(ctx.headerName.empty() && ctx.headerValue.empty());
			ctx.headerName.assign(at, length);
			state = STATE_RECV_HEADER_NAME;
		}
		else if (state == STATE_RECV_HEADER_NAME)
		{
			//continue receiving header name, append value
			assert(!ctx.headerName.empty());
			ctx.headerName.insert(ctx.headerName.end(), at, at + length);
			state = STATE_RECV_HEADER_NAME;
		}
		else if (state == STATE_RECV_HEADER_VALUE)
		{
			//previous header has completely received
			_this->_OnRecvHeader();

			assert(ctx.headerName.empty());
			ctx.headerName.assign(at, length);
			state = STATE_RECV_HEADER_NAME;
		}
		else
		{
			assert(!"invalid state, abort");
			*(int*)state = 0;
		}
		return 0;
	}

	/*parse the header value through the header name*/
	int HTTPParser::on_header_value(http_parser* parser, const char* at, size_t length)
	{
		HTTPParser* _this = (HTTPParser*)parser->data;
		auto& state = _this->_context.state;
		auto& ctx = _this->_context;

		if (state == STATE_RECV_HEADER_NAME)
		{
			assert(!ctx.headerName.empty() && ctx.headerValue.empty());
			state = STATE_RECV_HEADER_VALUE;
			ctx.headerValue.assign(at, length);
		}
		else if (state == STATE_RECV_HEADER_VALUE)
		{
			assert(!ctx.headerName.empty() && !ctx.headerValue.empty());
			state = STATE_RECV_HEADER_VALUE;
			ctx.headerValue.insert(ctx.headerValue.end(), at, at + length);
		}
		else
		{
			assert(!"invalid state, ignore this value");
			ctx.headerName.clear();
			ctx.headerValue.clear();
			state = STATE_RECV_BEGIN;
		}
		return 0;
	}

	void HTTPParser::_OnRecvHeader()
	{
		if (_context.headerName.empty() && _context.headerValue.empty()) {
			return;
		}
		_headers[_context.headerName].push_back(_context.headerValue);
		_context.headerName.clear();
		_context.headerValue.clear();
	}

	/*parse head completed*/
	int HTTPParser::on_headers_complete(http_parser* parser)
	{
		HTTPParser* _this = (HTTPParser*)parser->data;
		auto& ctx = _this->_context;

		if (ctx.state == STATE_RECV_HEADER_VALUE) {
			_this->_OnRecvHeader();
		}
		else {
			assert(!"invalid state");
		}

		//cleanup header receiving state
		ctx.headerName.clear();
		ctx.headerValue.clear();
		ctx.state = STATE_RECV_BODY;
		return 0;
	}

	/*record the url*/
	int HTTPParser::on_url(http_parser* parser, const char* at, size_t length)
	{
		HTTPParser* _this = (HTTPParser*)parser->data;
		_this->_url.assign(at, length);
		return 0;
	}

	/*handle the part of the body data*/
	int HTTPParser::on_body(http_parser* parser, const char* at, size_t length)
	{
		HTTPParser* _this = (HTTPParser*)parser->data;
		_this->_OnRecvBody(at, length);
		return 0;
	}

	void HTTPParser::_OnRecvBody(const void* data, size_t len)
	{
		if (_onBody) {
			_onBody(data, len);
		}
	}

	int HTTPParser::on_chunk_header(http_parser* parser)
	{
		return 0;
	}

	int HTTPParser::on_chunk_complete(http_parser* parser)
	{
		return 0;
	}

	/*received a complete message*/
	int32_t HTTPParser::on_message_complete(http_parser* parser)
	{
		HTTPParser* _this = (HTTPParser*)parser->data;
		_this->_context.state = STATE_RECV_DONE;
		return 0;
	}

	HTTPParser::HTTPParser(PARSER_TYPE type)
	{
		auto& settings = _context.settings;
		memset(&settings, 0, sizeof(settings));
		settings.on_body = on_body;
		settings.on_url = on_url;
		settings.on_status = on_status;
		settings.on_header_field = on_header_field;
		settings.on_header_value = on_header_value;
		settings.on_message_begin = on_message_begin;
		settings.on_headers_complete = on_headers_complete;
		settings.on_message_complete = on_message_complete;
		settings.on_chunk_header = on_chunk_header;
		settings.on_chunk_complete = on_chunk_complete;

		_context.parser.reset(new http_parser());
		http_parser_init(_context.parser.get(), type == PARSER_REQUEST ? HTTP_REQUEST : HTTP_RESPONSE);
		_context.parser->data = this;
	}

	HTTPParser::~HTTPParser()
	{
	}

	int HTTPParser::Parse(const void* data, int size)
	{
		if (data == NULL || size == 0) {
			return -1;
		}

		if (_context.state == STATE_FAIL) {
			return -1;
		}

		size_t len = http_parser_execute(_context.parser.get(), &_context.settings, (const char*)data, size);
		if (len != size) {
			_context.state = STATE_FAIL;
			return -1;
		}
		return 0;
	}

	int HTTPParser::GetState() const
	{
		return _context.state;
	}

	int HTTPParser::GetStatusCode() const
	{
		return _statusCode;
	}

	const Ark::HTTPHeaders& HTTPParser::GetHeaders() const
	{
		return _headers;
	}

	void HTTPParser::SetOnBody(const OnBodyFunction& onBody)
	{
		_onBody = onBody;
	}

}